#!/usr/bin/env python
# -*- coding: utf-8 -*-

from unipath import Path
from webpyte import ShowUserThisException
import Image, ImageFile
# To prevent this exception:
# IOError: encoder error -2 when writing image file
ImageFile.MAXBLOCK = 256 * 1024


def resize_image_if_larger_than(original, max_size):
    """Taking an Image object and a tuple (maxWidth, maxHeight),
    if necessary, resizes the image and returns it.
    """
    if isinstance(max_size[0], basestring): # Convert a tuple of strings
        max_size[0] = int(max_size[0])  # to a tuple of ints
        max_size[1] = int(max_size[1])
    if original.size[0] <= max_size[0] and original.size[1] <= max_size[1]:
        return original
    ratioOriginal = float(original.size[0]) / original.size[1]
    ratioMax      = float(max_size[0]) / max_size[1]
    if ratioOriginal > ratioMax:  # if width is the determinant
        return original.resize(
            (max_size[0], original.size[1]*max_size[0]/original.size[0]),
            Image.ANTIALIAS)
    else:                        # if height is the determinant
        return original.resize(
            (original.size[0]*max_size[1]/original.size[1], max_size[1]),
            Image.ANTIALIAS)



class Load(object):
    '''Abstract base class for Upload and Download.'''
    has_data = property(lambda self: len(self.data) > 0,
                        doc="Predicate: whether the file length is > 0.")
    
    def path():
        doc = "Gets or sets the path of the file to be saved."
        def fget(self):
            return Path(self.dir, self.stem + self.ext)
        def fset(self, path):
            p = Path(path)
            self.stem = p.stem
            if p.ext:  self.ext = p.ext.lower() # does not change to ''
            if not hasattr(self, 'ext'):
                self.ext = ''    # However, '' is better than undefined
            self.dir = p.parent
        return locals()
    path = property(**path())
    
    def get_stream(self):
        "Gets a file-like object."
        raise NotImplementedError()

    def resize_image_if_larger_than(self, max_size):
        """Taking a tuple (maxWidth, maxHeight), if necessary, resizes the
        uploaded image. The resulting Image object is stored, then returned.
        Usually you want to call write_file() next.
        """
        original = Image.open(self.get_stream())
        image = resize_image_if_larger_than(original, max_size)
        if image is original:
            self.resized = None
            return original
        else:
            self.resized = image
            return image
    
    def write_file(self):
        """Saves the uploaded file and returns its length.
        If empty file, does not save anything and returns zero.
        If uploaded image has been resized, saves the resized version and
        returns the length of the upload.
        *directory* is the destination folder.
        *filetitle* (optional) is the desired file name, minus extension.
        If None, the filename of the upload is used.
        """
        length = len(self.data)
        if length == 0:  return 0
        # Ensure directory existence
        self.dir.mkdir(parents=True)
        # If image has been resized, write it
        if self.resized:
            self.resized.save(self.path, quality=86, optimize=1)
        else:  # or write the original file
            f = open(self.path, "w")
            f.write(self.data)
            f.close()
        return length
    
    def status():
        doc = "Gets a status message to be displayed to the user."
        def fget(self):
            if not self.has_data:
                return u"O upload não foi feito: arquivo vazio."
            s = "Upload OK: %s (%i KB)." % (self.stem + self.ext,
                                            len(self.data) / 1024)
            if self.resized: s += u" Imagem redimensionada."
            return s
        return locals()
    status = property(**status())



class Upload(Load):
    '''Instances of this class make it easy to:
    * replace the title of a filename;
    * resize an uploaded image if larger than some dimensions;
    * save the file to a certain directory;
    * create a status message;
    * provide information such as the filename or whole path.
    
    In order to upload, the web form must have this attribute:
        enctype="multipart/form-data"
    '''
    
    def __init__(self, upload):
        """*upload* is the uploaded file."""
        if upload is None or not upload.filename:
            raise ShowUserThisException("No upload came through!")
        self.path = upload.filename
        self.upload = upload
        self.upload.file.seek(0)  # it may have already been read
        self.data = self.upload.file.read()
        self.resized = None
    
    def get_stream(self):
        self.upload.file.seek(0)  # it may have already been read
        return self.upload.file



import urllib2
from StringIO import StringIO

class Download(Load):
    '''Like the Upload class, except it downloads the file from a URL.'''
    def __init__(self, url):
        self.path = url.split('/')[-1]
        file = urllib2.urlopen(url)
        self.data = file.read()
        file.close()
        self.resized = None
    
    def get_stream(self):
        return StringIO(self.data)
